/*
 * Copyright 2010 The Regents of the University of California
 *
 *  All Rights Reserved
 */

/**
 *
 * File: dcswitch.c
 */
#include <openflow/openflow.h>
#include <unistd.h>
#include <config.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

#include "dcswitch.h"
#include "data-structures.h"
#include "ofpbuf.h"
#include "rconn.h"
#include "vlog.h"

#define THIS_MODULE VLM_dcswitch

void queue_tx(struct lswitch *sw, struct rconn *rconn, struct ofpbuf *b);

static int statsClientFD;

static DCSwitchConfig _dcswitchConfig;
void parseDCSwitchOptions(void);
void interpretConfigKeyValuePair(char *key, char *value);


void initializeDCSwitch(void)
{
	_dcswitchConfig.insertDownwardFlowEntries = false;
	_dcswitchConfig.idleTimeout = 0;
	_dcswitchConfig.hardTimeout = 0;
	_dcswitchConfig.flowEntryMatching = PIP;
	_dcswitchConfig.probeTimeout = 30;

    parseDCSwitchOptions();
}


/**
 * returns a system config/property for the max number of switches
 */
int getMaxSwitches(void) {

    return MAX_NUMBER_OF_SWITCHES;

}


void enqueueQueueTxMsg(struct lswitch *sw,
                       struct rconn *rconn,
                       struct ofpbuf *out) {

    struct DCSwitchMsg *data = NULL;

    // local variables for transmitting to local "udp" server
    char *serverAddr = "127.0.0.1";
    char msg[BUFSIZ];
    struct sockaddr_in serv_addr;

    // assert (sw != NULL);
    assert (rconn != NULL );
    assert (out != NULL);

    data = (struct DCSwitchMsg*) malloc (sizeof(struct DCSwitchMsg));

    data->sw = sw;
    data->rc = rconn;
    data->out = out;

    //DCQueue_enqueueData(&_queuedQueueTxMsgs, (void*)data);

    //////////////////////////////////////////////
    // let poll_block know that it should not
    // block since we've sent a msg to the waiting
    // statsListener socket!
    //////////////////////////////////////////////
    
    memset(msg,'\0',BUFSIZ);
    sprintf(msg,"pending stats req");
    
    bzero( (char *)&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = inet_addr(serverAddr);
    serv_addr.sin_port = htons(7110);

    if (sendto(statsClientFD,msg,strlen(msg),
               0,(struct sockaddr *)&serv_addr,
               sizeof(serv_addr)) == -1) {

        fprintf(stderr,"[%s] send msg to stats udp server\n",__PRETTY_FUNCTION__);
    } else {

        //VLOG_DBG("[%s] - successfully sent %s, notification, to stats listener",
        //         __PRETTY_FUNCTION__,
        //         msg); 
        
    }
}

/**
 * creates and initializes a udp datagram client
 */
int createDatagramClient(void) {

    struct sockaddr_in cli_addr;

    int socketFD = -1;

    if((socketFD = socket(AF_INET, SOCK_DGRAM,0)) < 0) {
        fprintf(stderr,"[%s] Failed to open client dgram socket\n",__PRETTY_FUNCTION__);
    }

    // setup client socket to fault-injector server

    bzero((char *)&cli_addr, sizeof(cli_addr));

    cli_addr.sin_family = AF_INET;
    cli_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    cli_addr.sin_port = htons(0);

    if (bind(socketFD, (struct sockaddr *)&cli_addr, sizeof(cli_addr)) < 0) {
        fprintf(stderr,"[%s] Failed to bind to local address\n",__PRETTY_FUNCTION__);
    }

    return socketFD;
}

void setConfigFile(char *fileName) {
    assert (fileName != NULL);
    _dcswitchConfig.configFileName = fileName;
}

/**
 * returns the k-property of the system
 */
int DCSwitch_getK(void) {
    return _dcswitchConfig.k;
}

int getIdleTimeout(void) {
    return _dcswitchConfig.idleTimeout;
}

int getHardTimeout(void) {
    return _dcswitchConfig.hardTimeout;
}
bool getInsertDownwardFlowEntries(void)
{
	return _dcswitchConfig.insertDownwardFlowEntries;
}

unsigned int getFlowEntryMatching(void)
{
	return _dcswitchConfig.flowEntryMatching;
}

int getProbeTimeout(void)
{
	return _dcswitchConfig.probeTimeout;
}
/**
 * reads in DCSwitch configuration settings
 */
void parseDCSwitchOptions(void) {
   
    char line[BUFSIZ];
    char *token;
    char key[250];
    char val[250];
    // int nbytes = 0;
    FILE *dcswitchConfigFile = NULL;

    //VLOG_WARN("[%s] - started file: %s ",__PRETTY_FUNCTION__, _dcswitchConfig.configFileName);


    if (_dcswitchConfig.configFileName != NULL) {
        dcswitchConfigFile = fopen("portland.config","r");

        //assert(dcswitchConfigFile != NULL);

        // parse each line in the config file and tokenize
        // the format is <key>:<value>
        while(fgets(line, BUFSIZ, dcswitchConfigFile ) != NULL) {
            token = strtok(line, ":\n");

            strcpy(key,token);

            token = strtok(NULL,"\n");
            strcpy(val,token);
        
            interpretConfigKeyValuePair(key, val);
            // reset temp buffers
            bzero(key,250);
            bzero(val,250);
            bzero(line,BUFSIZ);
        }

        fclose(dcswitchConfigFile);
    }

    VLOG_DBG("[%s] ended",__PRETTY_FUNCTION__);
}

void interpretConfigKeyValuePair(char *key, char *value) {

    assert (key != NULL);
    assert (value != NULL);

    if (strcmp(key,"configID") == 0) {
    
        _dcswitchConfig.configID = atoi(value);

        VLOG_DBG("[%s] - config ID = %d",
                  __PRETTY_FUNCTION__,
                  _dcswitchConfig.configID);

    } else if (strcmp(key,"k") == 0) {
        _dcswitchConfig.k = atoi(value);

        VLOG_DBG("[%s] - k = %d",
                  __PRETTY_FUNCTION__,
                  _dcswitchConfig.k);

    } else if (strcmp(key,"idle_timeout") == 0) {
        _dcswitchConfig.idleTimeout = atoi(value);

        VLOG_DBG("[%s] - idle_timeout = %d",
                  __PRETTY_FUNCTION__,
                  _dcswitchConfig.idleTimeout);

    } else if (strcmp(key,"hard_timeout") == 0) {
        _dcswitchConfig.hardTimeout = atoi(value);

        VLOG_DBG("[%s] - hard_timeout = %d",
                  __PRETTY_FUNCTION__,
                  _dcswitchConfig.hardTimeout);

    } else if (strcmp(key,"Probe Timeout") == 0) {
        _dcswitchConfig.probeTimeout = atoi(value);

        VLOG_DBG("[%s] - Probe Timeout = %d",
                  __PRETTY_FUNCTION__,
                  _dcswitchConfig.probeTimeout);

    } else if (strcmp(key,"pmac_filepath") == 0) {
        //strcpy(_dcswitchConfig.FileName , value);
        strcpy(_dcswitchConfig.FileName,value);

        VLOG_DBG("[%s] - FileName %s",
                  __PRETTY_FUNCTION__,
                  _dcswitchConfig.FileName);
    } else if (strcmp(key,"Insert Downward Flow Entries") == 0) {
        //strcpy(_dcswitchConfig.FileName , value);
	if((value[0] == 'Y'))
	{
		_dcswitchConfig.insertDownwardFlowEntries = true;
	}
	else if ((value[0] == 'N'))
	{
		_dcswitchConfig.insertDownwardFlowEntries = false;
	}
	else
	{
		VLOG_WARN("Inproper value!");
		_dcswitchConfig.insertDownwardFlowEntries = false;
	}

	if(_dcswitchConfig.insertDownwardFlowEntries)
		VLOG_DBG("value: %s, Insert Downward flow entry", value);
	else
		VLOG_DBG("value: %s, DONT insert downward flow entries", value);
		

        VLOG_DBG("[%s] - Insert Downward Flow Entries %d",
                  __PRETTY_FUNCTION__,
                  _dcswitchConfig.insertDownwardFlowEntries);
    } else if (strcmp(key,"Flow Entry Matching") == 0) {
        if (!strcmp(value, "PMAC"))
	{
		_dcswitchConfig.flowEntryMatching = PMAC;
	}
	else if(!strcmp(value, "PIP"))
	{
		_dcswitchConfig.flowEntryMatching = PIP;
	}
	else
	{
		VLOG_WARN("Inproper value!");
		_dcswitchConfig.flowEntryMatching = PMAC;
	}

	if(_dcswitchConfig.flowEntryMatching == PMAC)
		VLOG_WARN("value: %s, Insert flow entry on the basis of PMAC", value);
	else if(_dcswitchConfig.flowEntryMatching == PIP)
		VLOG_WARN("value: %s, Insert flow entries on the basis of PIP", value);
		

        VLOG_DBG("[%s] - Flow Entry matching is done of the basis of %d",
                  __PRETTY_FUNCTION__,
                  _dcswitchConfig.flowEntryMatching);
    } else {
        VLOG_DBG("[%s] - unknown key-value pair %s:%s",
                __PRETTY_FUNCTION__,
                key,
                value);

    }
}

